#include "stdafx.h"
#include "qg/qgasv.h"
#include "qg/qgrmodel.h"
#include "qgsmm_math.inl"

//////////////////////////////////////////////////////////////////////////
// SMA
KIMPL_SUBCLASS(qgSma, "Sma");

//
qgSma::qgSma()
{
	_BaseRoam(QGROAM_OBJ_3);

	_speed = 1.0f;
}

//
qgSma::~qgSma()
{
	kForEach(qgSmaUnit* p, _units)
	{
		kForEach(qgSmaKey* k, p->keys)
			delete k;

		delete p;
	}
}

//
void qgSma::Link(qgSmm* smm)
{
	_smm = smm;

	kForEach(qgSmaUnit* p, _units)
	{
		p->links.Reserve(p->keys.Count());

		kint cnt = p->keys.Count();
		for (kint i = 0; i < cnt; i++)
		{
			qgSmaKey* k = p->keys[i];
			qgSmmFrame* frm = smm->GetFrame(k->name.Data());
			p->links[i] = frm;
		}

		p->smm = smm;
	}
}

//
bool qgSma::IsAnimate()
{
	if (_isloop || _isblend)
		return true;
	else if (_cur)
		return _cur->cur > _cur->dur.min && _cur->cur < _cur->dur.max;
	else
		return false;
}

//
bool qgSma::IsFinish()
{
	if (_isloop || _isblend)
		return false;
	else if (_cur)
		return _cur->cur < _cur->dur.min || _cur->cur >= _cur->dur.max;
	else
		return true;
}

//
bool qgSma::SetUnit(const char* name)
{
	kForEach(qgSmaUnit* u, _units)
	{
		if (u->name.CompareTo(name, true) == 0)
		{
			_cur = u;
			return true;
		}
	}

	return false;
}

//
bool qgSma::SetUnitNth(kint n)
{
	if (n >= 0 && n < _units.Count())
	{
		_cur = _units[n];
		return true;
	}

	return false;
}

//
void qgSma::SetSpeed(float f)
{
	if (f < 0.0f)
	{
		_isrev = true;
		_speed = -f;
	}
	else
	{
		_isrev = false;
		_speed = f;
	}
}

//
void qgSma::TurnLoop(bool value)
{
	_isloop = value;
}

//
bool qgSma::Update(float adv)
{
	if (!_smm)
		return false;

	qgSmaUnit* u = _cur;
	float f;
	float s = _speed * adv;
	bool timeover = false;

	if (_isrev)
	{
		f = u->cur - (s * u->speed);

		if (f < u->dur.min)
		{
			if (_isloop)
				f = u->dur.max - f;
			else
			{
				f = u->dur.min;
				u->cur = f;
				timeover = true;
			}
		}
	}
	else
	{
		f = u->cur + (s * u->speed);

		if (f > u->dur.max)
		{
			if (_isloop)
				f -= u->dur.max;
			else
			{
				f = u->dur.max;
				u->cur = f;
				timeover = true;
			}
		}
	}

	//
	u->_UpdatePosition(f);

	//
	if (!timeover)
		u->cur = f;

	return timeover;
}

//
void qgSmaUnit::_UpdatePosition(float adv)
{
	kint cnt = links.Count();
	for (kint i = 0; i < cnt; i++)
	{
		qgSmmFrame* frm = links[i];
		if (!frm || !frm->IsAnim())
			continue;

		qgSmaKey* key = keys[i];

		//
		tpl::Vec3 loc, scl;
		tpl::Quat rot;
		kint z;

		if (key->locs.IsEmpty())
			loc = frm->GetLocation();
		else
		{
			for (z = key->locs.Count() - 1; z > 0; --z)
			{
				if (key->locs[z].t < adv)
					break;
			}

			if (z == key->locs.Count() - 1)
				loc = key->locs[z].v;
			else
			{
				float f = (adv - key->locs[z].t) / (key->locs[z + 1].t - key->locs[z].t);
				loc = tpl::Vec3::Lerp(key->locs[z].v, key->locs[z + 1].v, f);
			}

			loc += frm->GetOrgTM().loc;
		}

		if (key->rots.IsEmpty())
			rot = frm->GetRotation();
		else
		{
			for (z = key->rots.Count() - 1; z > 0; --z)
			{
				if (key->rots[z].t < adv)
					break;
			}

			if (z == key->rots.Count() - 1)
				rot = key->rots[z].q;
			else
			{
				float f = (adv - key->rots[z].t) / (key->rots[z + 1].t - key->rots[z].t);
				rot = tpl::Quat::Slerp(key->rots[z].q, key->rots[z + 1].q, f);
			}
		}

		if (key->scls.IsEmpty())
			scl = frm->GetScaling();
		else
		{
			for (z = key->scls.Count() - 1; z > 0; --z)
			{
				if (key->scls[z].t < adv)
					break;
			}

			if (z == key->scls.Count() - 1)
				scl = key->scls[z].v;
			else
			{
				float f = (adv - key->scls[z].t) / (key->scls[z + 1].t - key->scls[z].t);
				scl = tpl::Vec3::Lerp(key->scls[z].v, key->scls[z + 1].v, f);
			}
		}

		//
		//tpl::Mat4 m = tpl::Mat4::Transform(loc, rot, scl);
		frm->SetLocation(loc);
		frm->SetRotation(rot);
		frm->SetScaling(scl);
	}
}

//
qgSma* qgSma::New(const char* filename, kint flags)
{
	qgDevice* dev = qgDevice::CurrentDevice;

	kint size;
	kbyte* data = (kbyte*)dev->FileRead(filename, &size);

	if (!data)
		return NULL;

	qgSma* self = kobj_create(qgSma);
	if (!self->_PrivateRead520(data, size, 0))
	{
		self->Unload();
		self = NULL;
	}
	else
	{
		if (self->_units.IsHave())
			self->_cur = self->_units[0];
	}

	k_delete(data);

	return self;
}

